package we3d.renderer.cc 
{
	import we3d.View3d;
	import we3d.core.Camera3d;
	import we3d.scene.SceneObject;
	import we3d.mesh.Vertex;
	import we3d.mesh.Face;
	import we3d.math.Matrix3d;
	import we3d.mesh.UVCoord;
	import we3d.renderer.IObjectRenderer;
	import we3d.renderer.ClipUtil;
	
	public class CCClipFrustum extends ClipUtil implements IObjectRenderer
	{
		public function CCClipFrustum () {}
		
		public function render (view:View3d, o:SceneObject, cgv:Matrix3d) :void {
			
			var v:Camera3d = view.scene.cam;
			var r:Array = o.polygons;
			var L:int = r.length;
			var nearPlane:Number = v._nearClipping;
			var farPlane:Number = v._farClipping;
			
			var p:Face;
			var j:int;
			var clp1:Boolean;
			var clp2:Boolean;
			var pL:int;
			var k:int;
			var splitpts:Array;
			var splitpts2:Array;
			var uvs:Array;
			var splituvs:Array;
			var splituvs2:Array 
			var u:UVCoord;
			var u0:UVCoord;
			var splitface:Face;
			var e:Boolean;
			var ofc:int = o.frameCounter;
			
			var gv:Matrix3d = o.transform.gv;
			var camx:Number = v.transform.gv.m;
			var camy:Number = v.transform.gv.n;
			var camz:Number = v.transform.gv.o;
			var vtxa:Vertex;
			var vtxb:Vertex;
			var vtxs:Array;
			plw = nearPlane == 0 ? .0005 : nearPlane;
			var vt:Number = v.t;
			var vs:Number = v.s;
			var x:Number;
			var y:Number;
			var z:Number;
			
			for(j=0; j<L; j++) {
				
				p = r[j];
				pL = p.vLen;
				if(pL < 2) continue;
				
				vtxb = p.normal;
				vtxb.wz = gv.c * vtxb.x + gv.g * vtxb.y + gv.k * vtxb.z;
				vtxb.wy = gv.b * vtxb.x + gv.f * vtxb.y + gv.j * vtxb.z;
				vtxb.wx = gv.a * vtxb.x + gv.e * vtxb.y + gv.i * vtxb.z;
				
				vtxa = p.a;
				
				if(p.surface.hideBackfaces) {
					z = gv.c * vtxa.x + gv.g * vtxa.y + gv.k * vtxa.z + gv.o - camz;
					y = gv.b * vtxa.x + gv.f * vtxa.y + gv.j * vtxa.z + gv.n - camy;
					x = gv.a * vtxa.x + gv.e * vtxa.y + gv.i * vtxa.z + gv.m - camx;
					
					if(x*vtxb.wx + y*vtxb.wy + z*vtxb.wz < 0) continue;
				}
				
				vtxs = p.vtxs;
				
				for(k=0; k<pL; k++) {
					vtxb = vtxs[k];
					if(vtxb.frameCounter1 != ofc) {
						vtxb.wz = cgv.c * vtxb.x + cgv.g * vtxb.y + cgv.k * vtxb.z + cgv.o;
						vtxb.wy = cgv.b * vtxb.x + cgv.f * vtxb.y + cgv.j * vtxb.z + cgv.n;
						vtxb.wx = cgv.a * vtxb.x + cgv.e * vtxb.y + cgv.i * vtxb.z + cgv.m;
						vtxb.frameCounter1 = ofc;
					}
				}
				
				z = vtxa.wz;
				
				if(z < plw) {
					e = true;
					for(k=1; k<pL; k++) {
						vtxb = vtxs[k];
						if(vtxb.wz >= plw) {
							e = false;
							break;
						}
					}
					if(e) continue;
				}
				else if(z > farPlane) {
					e = true;
					for(k=1; k<pL; k++) {
						vtxb = vtxs[k];
						if(vtxb.wz <= farPlane) {
							e = false;
							break;
						}
					}
					if(e) continue;
				}
				
				if(vtxa.wy < -z) {
					e = true;
					for(k=1; k<pL; k++) {
						vtxb = vtxs[k];
						if(vtxb.wy >= -vtxb.wz) {
							e = false;
							break;
						}
					}
					if(e) continue;
				}
				else if(vtxa.wy > z) {
					e = true;
					for(k=1; k<pL; k++) {
						vtxb = vtxs[k];
						if(vtxb.wy <= vtxb.wz) {
							e = false;
							break;
						}
					}
					if(e) continue;
				}
				
				if(vtxa.wx < -z) {
					e = true;
					for(k=1; k<pL; k++) {
						vtxb = vtxs[k];
						if(vtxb.wx >= -vtxb.wz) {
							e = false;
							break;
						}
					}
					if(e) continue;
				}
				else if(vtxa.wx > z) {
					e = true;
					for(k=1; k<pL; k++) {
						vtxb = vtxs[k];
						if(vtxb.wx <= vtxb.wz) {
							e = false;
							break;
						}
					}
					if(e) continue;
				}
				
				e = true;
				for(k=0; k<pL; k++) {
					vtxb = vtxs[k];
					z = vtxb.wz;
					if(vtxb.wy > z || vtxb.wy < -z || vtxb.wx > z || vtxb.wx < -z || z < plw || z > farPlane) { // point culled
						e = false;
						break;
					}
				}
				if(e) {
					for(k=0; k<pL; k++) {
						vtxb = vtxs[k];
						if(vtxb.frameCounter2 != ofc) {
							vtxb.sx = vt + vtxb.wx/vtxb.wz * vt;
							vtxb.sy = vs - vtxb.wy/vtxb.wz * vs;
							vtxb.frameCounter2 = ofc;
						}
					}
					o.polyRenderer.drawPoly(view, p);
					continue;
				}
				
				splitpts = [];
				
				if(p.uvs == null) {
					// clip top plane ///////////////////////////
					vtxa = vtxs[0];
					clp1 = vtxa.wy > vtxa.wz;
					for(k=1; k<pL; k++) {
						
						vtxb = vtxs[k];
						clp2 = vtxb.wy > vtxb.wz;
						
						if(!clp1) splitpts.push( vtxa );
						if(clp1 != clp2) splitpts.push( splitVertexPlane(vtxa, vtxb, topPlane) );
						
						clp1 = clp2;
						vtxa = vtxb;
					}
					vtxb = vtxs[0];
					clp2 = vtxb.wy > vtxb.wz;
					if(!clp1) splitpts.push( vtxa );
					if(clp1 != clp2) splitpts.push( splitVertexPlane(vtxa, vtxb, topPlane) );
					
					
					
					pL = splitpts.length;
					if(pL < 2) continue;
					
					// clip bottom plane ///////////////////////////
					splitpts2 = [];
					vtxa = splitpts[0];
					clp1 = vtxa.wy < -vtxa.wz;
					for(k=1; k<pL; k++) {
						
						vtxb = splitpts[k];
						clp2 = vtxb.wy < -vtxb.wz;
						
						if(!clp1) splitpts2.push( vtxa );
						if(clp1 != clp2) splitpts2.push( splitVertexPlane(vtxa, vtxb, bottomPlane) );
						
						clp1 = clp2;
						vtxa = vtxb;
					}
					vtxb = splitpts[0];
					clp2 = vtxb.wy < -vtxb.wz;
					if(!clp1) splitpts2.push( vtxa );
					if(clp1 != clp2) splitpts2.push( splitVertexPlane(vtxa, vtxb, bottomPlane) );
					
					
					pL = splitpts2.length;
					if(pL < 2) continue;
					
					// clip left plane ///////////////////////////
					splitpts = [];
					vtxa = splitpts2[0];
					clp1 = vtxa.wx < -vtxa.wz;
					for(k=1; k<pL; k++) {
						
						vtxb = splitpts2[k];
						clp2 = vtxb.wx < -vtxb.wz;
						
						if(!clp1) splitpts.push( vtxa );
						if(clp1 != clp2) splitpts.push( splitVertexPlane(vtxa, vtxb, leftPlane) );
						
						clp1 = clp2;
						vtxa = vtxb;
					}
					vtxb = splitpts2[0];
					clp2 = vtxb.wx < -vtxb.wz;
					if(!clp1) splitpts.push( vtxa );
					if(clp1 != clp2) splitpts.push( splitVertexPlane(vtxa, vtxb, leftPlane) );
					
					
					
						
					pL = splitpts.length;
					if(pL < 2) continue;
					
					// clip right plane ///////////////////////////
					splitpts2 = [];
					vtxa = splitpts[0];
					clp1 = vtxa.wx > vtxa.wz;
					for(k=1; k<pL; k++) {
						
						vtxb = splitpts[k];
						clp2 = vtxb.wx > vtxb.wz;
						
						if(!clp1) splitpts2.push( vtxa );
						if(clp1 != clp2) splitpts2.push( splitVertexPlane(vtxa, vtxb, rightPlane) );
						
						clp1 = clp2;
						vtxa = vtxb;
					}
					vtxb = splitpts[0];
					clp2 = vtxb.wx > vtxb.wz;
					if(!clp1) splitpts2.push( vtxa );
					if(clp1 != clp2) splitpts2.push( splitVertexPlane(vtxa, vtxb, rightPlane) );
					
					
					
					pL = splitpts2.length;
					if(pL < 2) continue;
					
					// clip near plane ///////////////////////////
					splitpts = [];
					vtxa = splitpts2[0];
					clp1 = vtxa.wz < plw;
					for(k=1; k<pL; k++) {
						
						vtxb = splitpts2[k];
						clp2 = vtxb.wz < plw;
						
						if(!clp1) splitpts.push( vtxa );
						if(clp1 != clp2) splitpts.push( splitVertex(vtxb, vtxa) );
						
						clp1 = clp2;
						vtxa = vtxb;
					}
					vtxb = splitpts2[0];
					clp2 = vtxb.wz < v._nearClipping;
					if(!clp1) splitpts.push( vtxa );
					if(clp1 != clp2) splitpts.push( splitVertex(vtxb, vtxa) );
					
					pL = splitpts.length;
					
					if(pL> 1) {
						
						splitface = new Face();
						splitface.surface = p.surface;
						splitface.vLen = pL;
						splitface.vtxs = splitpts;
						splitface.a = splitpts[0];
						splitface.b = splitpts[1];
						splitface.c = splitpts[2];
						splitface.ax = p.ax;
						splitface.ay = p.ay;
						splitface.az = p.az;
						splitface.normal.wx = p.normal.wx;
						splitface.normal.wy = p.normal.wy;
						splitface.normal.wz = p.normal.wz;
						splitface.sortFar = p.sortFar;
						splitface.so = p.so;
						
						for(k=0; k<pL; k++) {
							vtxb = splitpts[k];
							if(vtxb.frameCounter2 != ofc) {
								vtxb.sx = vt + vtxb.wx/vtxb.wz * vt;
								vtxb.sy = vs - vtxb.wy/vtxb.wz * vs;
								vtxb.frameCounter2 = ofc;
							}
						}
						o.polyRenderer.drawPoly(view, splitface);
					}
					
				}
				else {
					
					// clip with uv
					uvs = p.uvs;
					
					splituvs = [];
					
					// clip top plane ///////////////////////////
					vtxa = vtxs[0];
					u0 = uvs[0];
					clp1 = vtxa.wy > vtxa.wz;
					
					for(k=1; k<pL; k++) {
						
						vtxb = vtxs[k];
						u = uvs[k];
						
						clp2 = vtxb.wy > vtxb.wz;
						
						if(!clp1) {
							splitpts.push( vtxa );
							splituvs.push( u0 );
						}
						if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, topPlane, u0, u, splitpts, splituvs);
						
						clp1 = clp2;
						vtxa = vtxb;
						u0 = u;
					}
					
					vtxb = vtxs[0];
					u = uvs[0];
					clp2 = vtxb.wy > vtxb.wz;
					if(!clp1) {
						splitpts.push( vtxa );
						splituvs.push( u0 );
					}
					if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, topPlane, u0, u, splitpts, splituvs);
					
					
					
					pL = splitpts.length;
					if(pL < 2) continue;
					
					// clip bottom plane ///////////////////////////
					splitpts2 = [];
					splituvs2 = [];
					
					vtxa = splitpts[0];
					u0 = splituvs[0];
					clp1 = vtxa.wy < -vtxa.wz;
					for(k=1; k<pL; k++) {
						
						vtxb = splitpts[k];
						u = splituvs[k];
						
						clp2 = vtxb.wy < -vtxb.wz;
						
						if(!clp1) {
							splitpts2.push( vtxa );
							splituvs2.push( u0 );
						}
						if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, bottomPlane, u0, u, splitpts2, splituvs2);
						
						clp1 = clp2;
						vtxa = vtxb;
						u0 = u;
					}
					
					vtxb = splitpts[0];
					u = splituvs[0];
					clp2 = vtxb.wy < -vtxb.wz;
					if(!clp1) {
						splitpts2.push( vtxa );
						splituvs2.push( u0 );
					}
					if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, bottomPlane, u0, u, splitpts2, splituvs2);
					
					
					pL = splitpts2.length;
					if(pL < 2) continue;
					
					// clip left plane ///////////////////////////
					splitpts = [];
					splituvs = [];
					vtxa = splitpts2[0];
					u0 = splituvs2[0];
					clp1 = vtxa.wx < -vtxa.wz;
					
					for(k=1; k<pL; k++) {
						
						vtxb = splitpts2[k];
						u = splituvs2[k];
						clp2 = vtxb.wx < -vtxb.wz;
						
						if(!clp1) {
							splitpts.push( vtxa );
							splituvs.push( u0 );
						}
						if(clp1 != clp2) {
							splitVertexUVPlane(vtxa, vtxb, leftPlane, u0, u, splitpts, splituvs);
						}
						
						clp1 = clp2;
						vtxa = vtxb;
						u0 = u;
					}
					vtxb = splitpts2[0];
					u = splituvs2[0];
					clp2 = vtxb.wx < -vtxb.wz;
					if(!clp1) {
						splitpts.push( vtxa );
						splituvs.push( u0 );
					}
					if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, leftPlane, u0, u, splitpts, splituvs);
					
					
					pL = splitpts.length;
					if(pL < 2) continue;
					
					// clip right plane ///////////////////////////
					splitpts2 = [];
					splituvs2 = [];
					vtxa = splitpts[0];
					u0 = splituvs[0];
					clp1 = vtxa.wx > vtxa.wz;
					for(k=1; k<pL; k++) {
						
						vtxb = splitpts[k];
						u = splituvs[k];
						clp2 = vtxb.wx > vtxb.wz;
						
						if(!clp1) {
							splitpts2.push( vtxa );
							splituvs2.push( u0 );
						}
						if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, rightPlane, u0, u, splitpts2, splituvs2);
						
						clp1 = clp2;
						vtxa = vtxb;
						u0 = u;
					}
					vtxb = splitpts[0];
					u = splituvs[0];
					clp2 = vtxb.wx > vtxb.wz;
					if(!clp1) {
						splitpts2.push( vtxa );
						splituvs2.push( u0 );
					}
					if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, rightPlane, u0, u, splitpts2, splituvs2);
					
					
					
					pL = splitpts2.length;
					if(pL < 2) continue;
					
					// clip near plane ///////////////////////////
					splitpts = [];
					splituvs = [];
					vtxa = splitpts2[0];
					u0 = splituvs2[0];
					clp1 = vtxa.wz < plw;
					for(k=1; k<pL; k++) {
						
						vtxb = splitpts2[k];
						u = splituvs2[k];
						clp2 = vtxb.wz < plw;
						
						if(!clp1) {
							splitpts.push( vtxa );
							splituvs.push( u0 );
						}
						if(clp1 != clp2)  splitVertexUV(vtxb, vtxa, u, u0, splitpts, splituvs);
						
						clp1 = clp2;
						vtxa = vtxb;
						u0 = u;
					}
					vtxb = splitpts2[0];
					u = splituvs2[0];
					clp2 = vtxb.wz < v._nearClipping;
					if(!clp1) {
						splitpts.push( vtxa );
						splituvs.push( u0 );
					}
					if(clp1 != clp2) splitVertexUV(vtxb, vtxa, u, u0, splitpts, splituvs);
					
					pL = splitpts.length;
					
					if(pL > 1) {
						
						splitface = new Face();
						splitface.surface = p.surface;
						splitface.vLen = pL;
						splitface.vtxs = splitpts;
						splitface.a = splitpts[0];
						splitface.b = splitpts[1];
						splitface.c = splitpts[2];
						splitface.ax = p.ax;
						splitface.ay = p.ay;
						splitface.az = p.az;
						splitface.normal.wx = p.normal.wx;
						splitface.normal.wy = p.normal.wy;
						splitface.normal.wz = p.normal.wz;
						splitface.sortFar = p.sortFar;
						splitface.so = p.so;
						splitface.uvs = splituvs;
						p.surface.initFace(splitface);
						
						for(k=0; k<pL; k++) {
							vtxb = splitpts[k];
							if(vtxb.frameCounter2 != ofc) {
								vtxb.sx = vt + vtxb.wx/vtxb.wz * vt;
								vtxb.sy = vs - vtxb.wy/vtxb.wz * vs;
								vtxb.frameCounter2 = ofc;
							}
						}
						
						o.polyRenderer.drawPoly(view, splitface);
					}
					
				}
				
			}
			
		}
	}
}